package com.googlecode.sparda.commons.io;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.googlecode.sparda.commons.constant.MemoryConstant;
import com.googlecode.sparda.commons.lang.MathUtils;

/**
 * Utility for IO.
 * @author Fabio Grucci (<i>alias</i> <b>Phoenix</b>)
 * @since 1.0
 * @version 1.0
 */
public final class IOUtils {
	
	private IOUtils() {/* Utility class */}
	
	/**
	 * Method that calculate the delta of the deep of the {@link File}.
	 * @param file1 The first {@link File}
	 * @param file2 The second {@link File}
	 * @return The depth difference of the {@link File}
	 */
	public static int depthDifferences(File file1, File file2) {
		int differences = 0;
		String pathToAnalize;
		pathToAnalize = file1.getAbsolutePath();
		for(int i=0; i<pathToAnalize.length(); i++)
			if(pathToAnalize.charAt(i) == File.separatorChar)
				differences++;
		pathToAnalize = file2.getAbsolutePath();
		for(int i=0; i<pathToAnalize.length(); i++)
			if(pathToAnalize.charAt(i) == File.separatorChar)
				differences--;
		return differences;
				
	}
	
	/**
	 * Method that calculate an optimal buffer length to manage the {@link File}.
	 * @param file The collection of file where calculate an optimal buffer length
	 * @return The optimal buffer length
	 */
	public static int calculateOptimalBufferLength(final Collection<File> file) {
		long size = 0;
		for(File f : file)
			size += f.length();
		return calculateOptimalBufferLength(file, size);
	}

	/**
	 * Method that calculate an optimal buffer length to manage the {@link File}.
	 * @param file The collection of file where calculate an optimal buffer length
	 * @param size The sum of the size of all the file
	 * @return The optimal buffer length
	 */
	public static int calculateOptimalBufferLength(final Collection<File> file, final long size) {
		return calculateOptimalBufferLength(file.size(), size);
	}

	/**
	 * Method that calculate an optimal buffer length to manage the {@link File}.
	 * @param fileNumber The number of the {@link File}
	 * @param size The sum of the size of all the file
	 * @return The optimal buffer length
	 */
	public static int calculateOptimalBufferLength(final int fileNumber, final long size) {
		return MathUtils.roundToExpBaseTwo(size/fileNumber);
	}
	
	/**
	 * Method that return a  {@link Map} <code>File</code>, <code>String</code> 
	 * organized in {@link File} -> relative path starting from the parentDirectory.
	 * @param file The {@link Collection} of file
	 * @param parentDirectory The parent directory base of the calculus
	 * @return The {@link Map} of {@link File} to {@link String} relative path starting from parentDirectory
	 */
	public static Map<File, String> relative(final Collection<File> file, final File parentDirectory) {
		final Map<File, String> mappa = new HashMap<File,String>(file.size());
		if(file.contains(parentDirectory))
			mappa.put(parentDirectory, parentDirectory.getName());
		final String pathMadre = parentDirectory.getPath();
		for(File f : file)
			mappa.put(f, StringUtils.removeStart(f.getName(), pathMadre));
		return mappa;
	}
	
	
	/**
	 * Close and flush the {@link OutputStream} and the {@link InputStream}.
	 * @param inputStream The {@link InputStream} to close
	 * @param outputStream The {@link OutputStream} to close
	 * @throws IOException If raised an {@link IOException}
	 */
	public static void close(final InputStream inputStream, final OutputStream outputStream) throws IOException {
		outputStream.flush();
		outputStream.close();
		inputStream.close();
	}
	
	/**
	 * Read all the {@link InputStream} and close it.
	 * @param inputStream The {@link InputStream} to read
	 * @throws IOException If raised an {@link IOException}
	 */
	public static void readAll(final InputStream inputStream) throws IOException {
		readAll(inputStream, MemoryConstant.ONE_KILOBYTE);
	}
	
	/**
	 * Read all the {@link InputStream} and close it.
	 * @param inputStream The {@link InputStream} to read
	 * @param bufferSize The number of byte on the read cycle
	 * @throws IOException If raised an {@link IOException}
	 */
	public static void readAll(final InputStream inputStream, final int bufferSize) throws IOException {
		final byte[] buffer = new byte[bufferSize];
		while(inputStream.read(buffer) != -1) {
			//nothing to do;
		}
		inputStream.close();
	}
	
}